<?php
/*

 QXCore is an extra light php framework released under MIT License
 You should have received a copy of the MIT License along with this program.
 If not, see http://www.opensource.org/licenses/mit-license.php

 LICENSE: MIT License

 @copyright   2009 Alex Movsisyan
 @author      Alex Movsisyan - alex@movsisyan.com
 @license     http://www.opensource.org/licenses/mit-license.php
 @link        http://www.qxcore.com/

*/

define('ROOT_DIR'               , str_replace("\\", "/", getcwd()));

// Define system varibles
define('CORE_KEY'               , true);
define('CORE_PHP_EXT'           , 'php');
define('CORE_VIEW_EXT'          , 'html');
define('CORE_XML_EXT'           , 'xml');
define('CORE_CONFIG_NAME'       , 'web.config');
define('CORE_ROUTES_NAME'       , 'routes');
define('CORE_MAIN_CONTROLLER'   , 'main');
define('CORE_VER'               , '0.92');

// Regex predefined patterns
define('__EMAIL__'              , '^[\w\-\.]{1,20}@[\w\-\.]{2,20}\.[a-zA-Z]{2,4}$');
define('__URL__'                , '^(?:https?|ftp)://[\w\-\.]+\.[a-zA-Z]{2,4}/?$');
define('__PASSWORD__'           , '^[\w]{4,12}$');
define('__EMPTY__'              , '^[\s]*$');
define('__NOTEMPTY__'           , '^[^\s]+$');

// Autoload core classess
function __autoload($name)
{
    $className = strtolower($name);
    $classArr = array('controller', 'view', 'model', 'module', 'extension');

    if(in_array($className, $classArr))
    {
        include_once (CORE_DIR . "/qxcore/{$className}." . CORE_PHP_EXT);
    }
}

//--

/**
 * For debugging purprouses
 */
if (! function_exists('pr'))
{
    function pr($object, $terminate = true)
    {
        echo("<pre>");

        var_dump($object);

        if ($terminate)
        {
            die("Time: " . T());
        }

        echo("</pre>");
    }
}

function T()
{
    return QXC::GetWorkTime();
}

function M()
{
    return QXC::GetMemoryUsage();
}

function __($key)
{
    return QXC()->Lang->Get($key);
}

/**
 * Get current instance
 * Alias to QXC::GetInstance() method
 *
 * @return QXC Returns QXCore instance
 */
function QXC()
{
    return QXC::GetInstance();
}

/**
 * @property QCache         $Cache
 * @property QConfig        $Config
 * @property QDb            $Db
 * @property QException     $Exception
 * @property QJson          $Json
 * @property QLog           $Log
 * @property QLang          $Lang
 * @property QMain          $Mail
 * @property QGlobal        $Global
 * @property QStorage       $Storage
 * @property QRoute         $Route
 * @property QUrl           $Url
 * @property QValidation    $Validation
 * @property QXml           $Xml
 * @property QZip           $Zip
 * @property View           $View
 * @property Model          $Model
 */
class QXC
{
    private static $QXC;
    private static $GLOBALS;
    private static $START_TIME;
    private $queryStringArray = array();

    function __construct()
    {
//        error_reporting(0);
//        ini_set('display_errors', 'off');

        self::$START_TIME = microtime(true);
        self::$QXC = $this;

        self::$GLOBALS = array();

        self::$GLOBALS['POST'] = $_POST;
        self::$GLOBALS['GET'] = $_GET;
        self::$GLOBALS['COOKIE'] = $_COOKIE;
        self::$GLOBALS['FILES'] = $_FILES;
        self::$GLOBALS['SERVER'] = $_SERVER;
        self::$GLOBALS['ERRORS'] = array();
        self::$GLOBALS['VALIDATION'] = array();
        self::$GLOBALS['BLOCKS'] = array();
        self::$GLOBALS['SCRIPTS'] = array();
        self::$GLOBALS['STYLES'] = array();
        self::$GLOBALS['QXC'] = array();
        self::$GLOBALS['EXTENSIONS'] = array();

        if (is_null($_SESSION))
        {
            session_name("QXC");
            session_start();

            self::$GLOBALS['SESSION'] = &$_SESSION;
        }

        $this->queryStringArray = $this->parseURI();

        $_GET = $_POST = $_REQUEST = $_COOKIE = $_FILES = array();

        // Initialize Custom Error Handler
        set_error_handler(array($this->Exception, ErrorHandler), E_ALL & ~E_NOTICE);

        // Initialize Custom Exception Handler
        set_exception_handler(array($this->Exception, ExceptionHandler));

        // Initialize Logger
        $this->Log->Trace('Start Loading: ' . START_TIME);

        // Initialize base config
        $this->Config->Open(CORE_CONFIG_NAME);

        // Initialize routes
        $this->Route->Open(CORE_ROUTES_NAME);

        if(!$this->Global->Get('lang')->IsEmpty() && $this->Global->Get('lang')->Validate("[\w]{2,15}"))
        {
            $this->Global->Session("qxc_lang", $this->Global->Get('lang')->ToString());
        }

        if(!$this->Global->Session("qxc_lang")->IsEmpty())
        {
            $this->Lang->SetLang($this->Global->Session("qxc_lang")->ToString());
        }

        // Is in Debug mode
        define("DEBUG", (bool)$this->Config->debug);

        // Compress output
        define("GZIP_IT", strstr($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') && (bool)$this->Config->compress_output);

        if (GZIP_IT)
        {
            ob_start();
            ob_implicit_flush(0);
        }

        // Try to load necessary module
        $isLoaded = $this->loadModule();

        if (!$isLoaded)
        {
        // Maybe we can load an controller?
            $isLoaded = $this->loadController();
        }

        if (!$isLoaded)
        {
            $isLoaded = $this->loadView();
        }

        if(!$isLoaded)
        {
            throw new QWebException('404', 'Page not found', join("/", $this->queryStringArray));
        }

        if (GZIP_IT)
        {
            $content = ob_get_clean();

            $content = gzencode($content, 8);

            $this->Global->Header('Content-Encoding', 'gzip');
            $this->Global->Header('Content-Length', strlen($content));
        }

        $this->Global->Header('X-Powered', 'QXCore');
        $this->Global->Header('X-Stop', 'Hammertime!');

        if(DEBUG)
        {
            $this->Global->Header('X-Time', T());
        }

        die($content);
    }

    private function loadView()
    {
        $viewName = $this->getPart(0);
        $template = $this->Config->default_template;

        if (empty($viewName))
        {
            $viewName = CORE_MAIN_CONTROLLER;
        }

        $viewFile = "{$viewName}." . CORE_VIEW_EXT;

        if($path = $this->pathExist($this->Config->views, $viewFile))
        {            
            if($template)
            {
                $view = View::Load($template, array(), dirname($path));
                $view->SetBlock('content', $viewName);
            }
            else
            {
                $view = View::Load($viewName, array(), dirname($path));
            }

            $view->Render();

            return true;
        }

        return false;
    }

    private function loadModule()
    {
        $moduleName = $this->getPart(0);

        if($path = $this->Route->Get($moduleName))
        {
            if($modulePath = $this->pathExist($this->Config->modules, $moduleName))
            {
                $this->setGlobal("ModuleName", $moduleName, "QXC");
                $this->setGlobal("ModulePath", $modulePath, "QXC");

                $controllerPath = "{$modulePath}/controllers";

                return Controller::Load(array_slice($this->queryStringArray, 1), $controllerPath);
            }
        }

        return false;
    }

    private function loadController()
    {
        return Controller::Load($this->queryStringArray,
        $this->Config->controllers);
    }

    public function getGlobal($key, $globalName, $persistance = false)
    {
        if($persistance)
        {
            if (!is_null($key) && !empty($key))
            {
                return (array_key_exists($key, self::$GLOBALS['SESSION'][$globalName]) ? self::$GLOBALS['SESSION'][$globalName][$key] : NULL);
            }
            else
            {
                return (array_key_exists($globalName, self::$GLOBALS['SESSION']) ? self::$GLOBALS['SESSION'][$globalName] : NULL);
            }
        }
        else
        {
            if (!is_null($key) && !empty($key))
            {
                return (array_key_exists($key, self::$GLOBALS[$globalName]) ? self::$GLOBALS[$globalName][$key] : NULL);
            }
            else
            {
                return (array_key_exists($globalName, self::$GLOBALS) ? self::$GLOBALS[$globalName] : NULL);
            }
        }
    }

    public function setGlobal($key, $value, $globalName, $pers = false)
    {
        if($pers)
        {
            if (!empty($key))
            {
                self::$GLOBALS['SESSION'][$globalName][$key] = $value;
            }
            else
            {
                self::$GLOBALS['SESSION'][$globalName][] = $value;
            }
        }
        else
        {
            if (!empty($key))
            {
                self::$GLOBALS[$globalName][$key] = $value;
            }
            else
            {
                self::$GLOBALS[$globalName][] = $value;
            }
        }
    }

    public function delGlobal($globalName, $pers = false)
    {
        if($pers)
        {
            self::$GLOBALS['SESSION'][$globalName] = array();
        }
        else
        {
            self::$GLOBALS[$globalName] = array();
        }
    }

    public function getPart($num, $equal = '')
    {
        if (is_numeric($num))
        {
            if(!empty ($equal))
            {
                return ($this->queryStringArray[$num] == $equal) ? true : false;
            }

            return $this->queryStringArray[$num];
        }
    }

    public static function GetInstance()
    {
        if (!self::$QXC instanceof QXC)
        {
            self::$QXC = new self();
        }

        return self::$QXC;
    }

    // Private Methods
    protected function loadExtension($name)
    {
        if($ext = $this->getGlobal($name, 'EXTENSIONS'))
        {
            return $ext;
        }
        else
        {
            $extDir = CORE_DIR . '/system/' . strtolower($name);
            $extPath = $extDir . '/index.php';

            if (file_exists($extPath))
            {
                include_once ($extPath);

                $qName = "Q{$name}";

                $ext = new $qName(self::$QXC, $extDir);

                $this->setGlobal($name, $ext, 'EXTENSIONS');

                return $ext;
            }
        }

        return false;
    }

    private function parseURI()
    {
        $scheme = parse_url(WEB_URL);
        $str = str_replace($scheme['path'], "", $_SERVER['REQUEST_URI']);

        $pos = strpos($str, "?");

        if((bool)$pos)
        {
            $str = substr($str, 0, $pos);
        }

        $parts = preg_split("/\//", $str, -1, PREG_SPLIT_NO_EMPTY);

        return array_map(create_function('$str', 'return (preg_match("/^[\w\-\.]{1,50}$/", trim($str)))?$str:NULL;'), $parts);
    }

    public function pathExist($path, $name = "")
    {
        if (is_array($path))
        {
            foreach ($path as $val)
            {
                $dirPath = ($val{0} == "/" || $val{0} == "D" || $val{0} == "C") ? $val : ROOT_DIR . "/{$val}";                

                if(is_array($name))
                {
                    foreach ($name as $n)
                    {
                        if (file_exists($path = $dirPath . "/{$n}"))
                        {
                            return $path;
                        }
                    }
                }
                else
                {
                    if (file_exists($path = $dirPath . "/{$name}"))
                    {
                        return $path;
                    }
                }
            }
        }
        else
        {
        // TODO: fix this
            $dirPath = ($path{0} == "/" || $path{0} == "D" || $path{0} == "C") ? $path : ROOT_DIR . "/{$path}";

            if(is_array($name))
            {
                foreach ($name as $n)
                {
                    if (file_exists($path = $dirPath . "/{$n}"))
                    {
                        return $path;
                    }
                }
            }
            else
            {
                if (file_exists($path = $dirPath . "/{$name}"))
                {
                    return $path;
                }
            }
        }

        return false;
    }

    public static function GetFilesList($dir, $ext = '*')
    {
        $arr = array();

        if ($handle = opendir($dir))
        {
            while (false !== ($file = readdir($handle)))
            {                
                if ($file{0} != "." && preg_match("/\.{$ext}$/", $file))
                {                    
                    $arr[] = $file; //preg_replace("/{$ext}$/", "", $file);
                }
            }

            closedir($handle);
        }

        return $arr;
    }

    public static function GetMemoryUsage()
    {
        $mem_usage = memory_get_usage(true);

        if ($mem_usage < 1024)
            return $mem_usage." b";
        elseif ($mem_usage < 1048576)
            return round($mem_usage/1024,2) . " Kb";
        else
            return round($mem_usage/1048576,2) . " Mb";
    }


    public static function GetWorkTime()
    {
        return substr((microtime(true) - self::$START_TIME), 0, 6);
    }

    // Magic Methods
    protected function __get($name)
    {
        if (isset($name) && ctype_alnum($name))
        {
            if(!$object = $this->loadExtension($name))
            {
                die("ERROR: Extension '{$name}' not found!");
            }

            return $this->{$name} = $object;
        }
        else
        {
        // TODO Add exception handler
            return '';
        }
    }
}

// Start point
QXC();
